import json
import uuid
from os import path, makedirs
from pathlib import Path

import tablib
from django.conf import settings

from gis.common import track_logging
from gis.common.exceptions import BizException
from gis.common.json import JsonEncoder
from gis.common.timer import api as timer_api
from itpay.core.agency import transation_service, agency_service
from itpay.core.agency.const import AgencyOrderStatusCN, PayTypeCN, ReceiptTypeCN, ComplaintTypeCN
from itpay.core.mch import service as mch_service
from itpay.core.recharge_order import order_service, recharge_comment_service
from itpay.core.recharge_order.const import TASK_EXPORT_DATA, OrderStatusCN
from itpay.server_admin.view.agency.helper import wrapper_agency_name, wrapper_name as wrapper_agency_complian_name
from itpay.server_admin.view.recharge_order.helper import wrapper_agency_info, \
    wrapper_name as wrapper_comment_agency_name
from .const import *
from .models import *

_LOGGER = track_logging.getLogger(__name__)


def _redirect_to_file(items, filename, header):
    """
    待思考，能不能已追加写文件的方式实现，而不是先把旧文件读出来，占用内存
    :param items:
    :param filename:
    :param header:
    :return:
    """
    if not Path(settings.EXPORT_PATH).exists():
        makedirs(settings.EXPORT_PATH)

    file_path = path.join(settings.EXPORT_PATH, filename)

    if Path(file_path).exists():
        data = tablib.Dataset().load(open(file_path, 'rb+').read())
        data.extend(items)
    else:
        data = tablib.Dataset(*items)

    if header:
        data.headers = header
    with open(file_path, 'wb') as f:
        f.write(data.export('xlsx'))


def _gen_filename(file_name):
    return str(file_name) + '.xlsx'


def _gen_task_hash_code():
    """
    待优化，更具搜索条件生成hash_code,避免重复生成报表
    :return:
    """
    return uuid.uuid4()


def _get_export_task_by_id(export_id):
    return ExportTask.objects.get(pk=export_id)


def _get_export_task_by_task_hash_code(task_hash_code):
    return ExportTask.objects.filter(task_hash_code=task_hash_code).first()


def export_admin_agency_recharge(page_no, page_size, query_conditions):
    """
    导出总代充值
    :param page_no:
    :param page_size:
    :param query_conditions:
    :return:
    """
    count, recharges = transation_service.list_apply_recharge(page_no, page_size, order_by='id', **query_conditions)
    items = [wrapper_agency_name(e) for e in recharges]
    header = ["订单id", "代理账号", "申请金额", "申请前余额", "申请时间", "当前状态", "到账时间", "最后更新时间", "追分金额", "备注"]
    result = []
    for e in items:
        result.append(
            [e['id'], e['agency']['user_name'], e['amount'] / 100, e['before_amount'] / 100, e['created_at'],
             AgencyOrderStatusCN.get(e['status']), e['success_at'], e['updated_at'], e['chase_amount'] / 100,
             e['reason']])

    return count, header, result


def export_normal_agency_recharge(page_no, page_size, query_conditions):
    """
    导出子代充值
    :param page_no:
    :param page_size:
    :param query_conditions:
    :return:
    """
    count, recharges = transation_service.list_apply_recharge(page_no, page_size, order_by='id', **query_conditions)
    items = [wrapper_agency_name(e) for e in recharges]

    header = ["订单id", "代理账号", "所属总代", "申请金额", "申请前余额", "申请时间", "当前状态", "到账时间", "最后更新时间", "追分金额", "备注"]
    result = []
    for e in items:
        result.append(
            [e['id'], e['agency']['user_name'], e['agency']['parent'].get("user_name"), e['amount'] / 100,
             e['before_amount'] / 100,
             e['created_at'],
             AgencyOrderStatusCN.get(e['status']), e['success_at'], e['updated_at'], e['chase_amount'] / 100,
             e['reason']])

    return count, header, result


def export_user_recharge(page_no, page_size, query_conditions):
    """
    导出用户充值
    :param page_no:
    :param page_size:
    :param query_conditions:
    :return:
    """
    count, recharges = order_service.list_orders(page_no, page_size, order_by='id', **query_conditions)
    items = [wrapper_agency_info(e) for e in recharges]

    header = ["订单id", "商户订单ID", "所属商户", "用户ID", "代理账号", "所属总代", "是否自助", "支付方式", "收款账号ID", "订单金额", "充值金额", "到账金额",
              "追分金额", "订单状态", "备注", "创建时间", "到账时间", "最后更新时间"]
    result = []
    for e in items:
        result.append(
            [e['id'], e['mch_recharge_id'], e['mch_name'], e['user_id'], e['agency_name'],
             e['agency']['parent']["user_name"], e['auto_arrival'], PayTypeCN.get(e['pay_type']),
             e['receipt_address_id'],
             e['amount'] / 100,
             e['real_amount'] / 100, e['recharge_amount'] / 100, e['chased_amount'] / 100,
             OrderStatusCN.get(e['status']), e["remark"], e["created_at"], e["success_at"], e["updated_at"]])

    return count, header, result


def export_user_recharge_comment(page_no, page_size, query_conditions):
    """
    导出用户充值评价
    :param page_no:
    :param page_size:
    :param query_conditions:
    :return:
    """
    count, comments = recharge_comment_service.list_pay_order_comment(page_no, page_size, order_by='id',
                                                                      **query_conditions)
    items = [wrapper_comment_agency_name(e) for e in comments]

    header = ["评价ID", "用户ID", "所属商户", "关联代理", "所属总代", "是否自动评价", "订单ID", "申请金额", "评分", "评价时间", "评价内容", "订单完成时间"]
    result = []
    for e in items:
        mch = mch_service.get_mch(e['mch_id'])
        result.append(
            [e['id'], e['user_id'], mch['name'], e['agency']['user_name'],
             e['agency']['parent']["user_name"], e['auto_comment'], e['order_id'],
             e['amount'] / 100, e['comment_star'], e['created_at'],
             e['comment_content'],
             e['success_at']])

    return count, header, result


def export_user_complain(page_no, page_size, query_conditions):
    """
    导出用户投诉
    :param page_no:
    :param page_size:
    :param query_conditions:
    :return:
    """
    count, complains = agency_service.list_complaint_agency(page_no, page_size, order_by='id', **query_conditions)
    items = [wrapper_agency_complian_name(e) for e in complains]

    header = ["投诉ID", "用户ID", "所属商户", "投诉代理", "所属总代", "投诉类型", "投诉内容", "投诉时间", "处理状态", "处理结果", "最后更新时间"]
    result = []
    for e in items:
        mch = mch_service.get_mch(e['mch_id'])

        result.append(
            [e['id'], e['user_id'], mch['name'], e['agency']['user_name'],
             e['agency']['parent']["user_name"], ComplaintTypeCN.get(e['complaint_types'][0]), e['complaint_content'],
             e['created_at'],
             e['has_done'], e['remark'],
             e['updated_at']])

    return count, header, result


def export_agency_stats(page_no, page_size, query_conditions):
    count, stats = transation_service.get_agency_stats_report(page_no, page_size,
                                                              **query_conditions)

    header = ["日期", "所属平台", "所属总代", "所属子代",
              "申请次数", "申请金额", "发起人数",
              "发起次数", "充值人数", "充值次数",
              "充值金额", "发起成功率", "订单成功率",
              "追分次数", "追分金额", "评价次数",
              "好评次数", "差评次数", "投诉次数"]

    result = []
    for e in stats:
        result.append(
            [e['day'], e['mch_id'], e['parent_id'], e['agency_id'],
             e['agency_recharge_success_count'], e['agency_recharge_success_amount'] / 100,
             e['user_recharge_create_count'],
             e['user_recharge_create_times'], e['user_recharge_success_count'], e['user_recharge_success_times'],
             e['user_recharge_success_amount'] / 100, e['user_recharge_apply_rate'], e['user_recharge_success_rate'],
             e['user_recharge_chase_count'], e['user_recharge_chase_amount'] / 100, e['user_recharge_comment_count'],
             e['user_recharge_good_comment_count'], e['user_recharge_bad_comment_count'], e['agency_complain_count']])

    return count, header, result


def create_export_task(filter_conditions, export_type):
    assert isinstance(export_type, ExportType)
    task_hash_code = _gen_task_hash_code()
    task = _get_export_task_by_task_hash_code(task_hash_code)
    if task:
        return task.id
    file_name = _gen_filename(task_hash_code)
    e = ExportTask(task_hash_code=task_hash_code, file_name=file_name,
                   filter_conditions=json.dumps(filter_conditions, cls=JsonEncoder),
                   export_type=export_type.value, status=ExportStatusType.NEW.value)

    e.save()
    timer_api.add_task(TASK_EXPORT_DATA, e.id)
    return e.to_dict()


def get_task_by_id(task_id):
    return _get_export_task_by_id(task_id).to_dict()


EXPORT_QUERY_FUNC = {ExportType.ADMIN_AGENCY_RECHARGE: export_admin_agency_recharge,
                     ExportType.NORMAL_AGENCY_RECHARGE: export_normal_agency_recharge,
                     ExportType.USER_RECHARGE: export_user_recharge,
                     ExportType.USER_RECHARGE_COMMENT: export_user_recharge_comment,
                     ExportType.USER_COMPLAIN: export_user_complain,
                     ExportType.AGENCY_STATS: export_agency_stats,
                     }


def get_filter_func(export_type):
    return EXPORT_QUERY_FUNC[ExportType(export_type)]


def execute_export(export_id):
    export = _get_export_task_by_id(export_id)
    if export.status != ExportStatusType.NEW.value:
        return
    page_no = 1
    page_size = 1000
    query_conditions = json.loads(export.filter_conditions) if export.filter_conditions else dict()
    func = get_filter_func(export.export_type)
    try:
        while True:
            _, header, items = func(page_no, page_size, query_conditions)
            if items:
                _redirect_to_file(items, export.file_name, header=header)
            page_no += 1
            if page_no > 50 or not items:
                break
    except Exception as e:
        _LOGGER.info("export task fail {}".format(export_id), exc_info=True)
        export.status = ExportStatusType.FAIL.value
        export.save()
        raise e

    export.status = ExportStatusType.SUCCESS.value
    export.save()


class ExportHandler(timer_api.TaskHandler):
    def handle(self, task):
        export_task_id = int(task.biz_num)
        try:
            execute_export(export_task_id)
        except BizException as e:
            _LOGGER.info('ExportHandler, export task id '.format(export_task_id))

    def get_biz_code(self):
        return TASK_EXPORT_DATA
